package demo5;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 姚东名
 * Date: 2024-06-11
 * Time: 20:51
 */

class Data1 {
    public int year;
    public int month;
    public int day;

    public void setDay(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }
}

class Data {
    public int year;
    public int month;
    public int day;

    //构造方法：
    //名字与类名相同，没有返回值类型设置为void也不行
    //一般情况下使用public修饰
    //在创建对象是由编译器自动调用的，并且在对象的生命周期内置调用一次
    public Data(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("Data(int, int, int)方法被调用了...");
    }

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        //此处创建了一个Data类型的对象，并没有显式调用方法
        Data data = new Data(2024, 6, 12);//输出Data(int, int, int)方法被调用了
        data.printData();//2024-6-12
    }
}

/**
 * 5. 对象的构造及初始化
 */
public class Test {
    //5.1如何初始化对对象
    //通过前面知识点的学习知道，在Java方法内部定义一个局部变量时，必须要初始化，否则会编译失败。
    public static void main1(String[] args) {
        int a;
        //System.out.println(a);
    }

    //D:\Java\java_practice\J20240612\src\demo5\Test.java:19:28java: 可能尚未初始化变量a

    //要让上述代码通过编译，非常简单，只需在正式使用a之前，给a设置一个初始值即可。如果是对象：
    public static void main(String[] args) {
        Data1 data1 = new Data1();
        data1.setDay(2021, 6, 16);
        data1.printData();
    }

    // 代码可以正常通过编译
    /*需要调用之前写的SetDate方法才可以将具体的日期设置到对象中。通过上述例子发现两个问题：
    1. 每次对象创建好后调用SetData方法设置具体日期，比较麻烦，那对象该如何实例化？
    2. 局部变量必须要初始化才能使用，为什么字段声明之后没有给值依然可以使用？
     */
    //5.2构造方法
    //5.2.1概念
    //构造方法(也称之为构造器)是一个特殊的成员方法，名字必须与类名相同，在创建对象时，在编译器自动调用，并且在整个对象的生命周期内只能使用一次。
    //注意：构造方法的作用就是对对象中的成员进行初始化，并不负责给对象开辟空间。

    //5.2.2特性
    /*
    1. 名字必须与类名相同
    2. 没有返回值类型，设置为void也不行
    3. 创建对象时由编译器自动调用，并且在对象的生命周期内只调用一次(相当于人的出生，每个人只能出生一次)
    4. 构造方法可以发生重载(用户根据自己的需求提供不同的参数的构造方法)
     */
}

class Data3 {
    public int year;
    public int month;
    public int day;

    //无参构造方法
    public Data3() {
        this.year = 1999;
        this.month = 1;
        this.day = 1;
    }

    //带有三个参数的构造方法
    public Data3(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("Data(int, int, int)方法被调用了...");
    }

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        //此处创建了一个Data类型的对象，并没有显式调用方法
        Data3 data3 = new Data3();
        data3.printData();//1999-1-1
    }
}
//上述的两个构造方法：名字相同，参数列表不同，因此构成了方法重载。

//5.如果用户没有显示定义，编译器会生成一份默认的构造方法，生成的默认构造方法一定是无参的。
class Data4 {
    public int year;
    public int month;
    public int day;

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        //此处创建了一个Data类型的对象，并没有显式调用方法
        Data4 data4 = new Data4();
        data4.printData();
    }
}

//上述Data4类中，没有定义任何构造方法，编译器会默认生成一个不带参数的构造方法
//注意：一旦用户定义，编译器则不再生成。
class Data5 {
    public int year;
    public int month;
    public int day;

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        //如果编译器会生成，则生成的构造方法一定是无参的
        //则此处创建对象是可以通过编译的
        //但实际情况：编译期报错
        Data5 data5 = new Data5();
        data5.printData();
    }
}

/*
Error:(26, 18) java: 无法将类 extend01.Date中的构造器 Date应用到给定类型;
需要: int,int,int
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
*/

//6.构造方法中，可以通过this调用其他构造方法来简化代码
class Data6 {
    public int year;
    public int month;
    public int day;

    //无参构造方法 -> 内部给各个成员赋值初始化，该部分成员功能与三个参数的构造方法是一样的。
    //此处可以在无参的构造方法通过this来调用带三个参数的构造方法
    //注意：this(1900, 1, 1)语句 必须是构造方法中的第一条语句
    public Data6() {
        this(1900, 1, 1);
        /*this.year = 1999;
        this.month = 1;
        this.day = 1;*/
    }

    //带有三个参数的构造方法
    public Data6(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("Data(int, int, int)方法被调用了...");
    }

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        //此处创建了一个Data类型的对象，并没有显式调用方法
        Data6 data6 = new Data6();
        data6.printData();//1999-1-1
    }
}
//注意：this(1900, 1, 1)语句 必须是构造方法中的第一条语句

//不能形成闭环
/*
public Data6() {
    this(1900, 1, 1);
}
    //带有三个参数的构造方法
    public Data6 (int year, int month, int day) {
        this();
    }*/

/*
无参构造器调用三个参数的构造器，而三个参数构造器有调用无参的构造器，形成构造器的递归调用
编译报错：Error:(19, 12) java: 递归构造器调用
*/

//7.绝大多数情况下使用public来修饰，特殊场景下会被private修饰。

//5.3
//在上文中提出的第二个问题：为什么局部变量在使用时必须要初始化，而成员变量可以不用呢？
class Data7 {
    public int year;
    public int month;
    public int day;

    public Data7(int year, int month, int day) {
        //成员变量在定义的时候，并没有给初始值，为什么就可以使用呢？
        System.out.println(this.year);
        System.out.println(this.month);
        System.out.println(this.day);
    }

    public static void main(String[] args) {
        //此处a并没有初始化，编译时报错：
        //Error: java: 可能尚未初始化变量a
        /*int a;
        System.out.println(a);*/
        Data7 data7 = new Data7(2024, 6, 12);
    }
}

//要搞清楚这个过程，就需要知道 new 关键字背后所发生的一些事情：
/*
1. 检测对象对应的类是否加载了，如果没有加载则加载。
2. 为对象分配内存空间。
3. 处理并发安全问题
   比如：多个线程同时申请对象，JVM要保证给对象分配的空间不冲突数据类型 默
4. 初始化所分配的空间
   即：对象空间被申请好之后，对象中包含的成员已经设置好了初始值，比如：
5. 设置对象信息(关于对象内存模型的)
6. 调用构造方法，对象中各个成员赋值
 */

//5.4就地初始化

//在声明成员变量时，就直接给出了初始值。
class Date8 {
    public int year = 1900;
    public int month = 1;
    public int day = 1;

    public Date8() {
    }

    public Date8(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public void printData() {
        System.out.println(this.year + "-" + this.month + "-" + this.day);
    }

    public static void main(String[] args) {
        Date8 d1 = new Date8(2021, 6, 9);
        d1.printData();
        Date8 d2 = new Date8();
        d2.printData();
    }
}
//注意：代码编译完成后，编译器会将所以给成员初始化的这些语句添加到各个构造方能法中。